﻿#region << 文 件 说 明 >>

/*----------------------------------------------------------------
// 文件名称：VampirewalConfig
// 创 建 者：杨程
// 创建时间：2021/9/24 11:10:56
// 文件版本：V1.0.0
// ===============================================================
// 功能描述：
//
//
//----------------------------------------------------------------*/

#endregion



namespace Vampirewal.Core.Components;

internal sealed class VampirewalCoreAppConfig : IVampirewalCoreAppConfig
{
    #region Fields and Properties

    private readonly string ConfigPath = Path.Combine(Directory.GetCurrentDirectory(), "Configuration");
    private readonly ConcurrentDictionary<string, IOptions> OptionDic = new();
    private FileSystemWatcher _watcher;

    // 防抖机制（每个文件独立计时器）
    private readonly ConcurrentDictionary<string, TaskCompletionSource<bool>> _fileDebounceTasks = new();
    private const int DebounceMilliseconds = 500;


    private readonly ConcurrentDictionary<string, Type> TypeMap = new();
    private readonly ConcurrentDictionary<Type, MethodInfo> HotReloadMethods = new();

    public List<IOptions> Options { get; set; } = new List<IOptions>();
    #endregion

    #region Constructor

    public VampirewalCoreAppConfig()
    {
        EnsureConfigurationDirectory();
        InitializeFileWatcher();
    }

    private void EnsureConfigurationDirectory()
    {
        if (!Directory.Exists(ConfigPath))
        {
            Directory.CreateDirectory(ConfigPath);
        }
    }

    private void InitializeFileWatcher()
    {
        _watcher = new FileSystemWatcher(ConfigPath, "*.json")
        {
            NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName,
            EnableRaisingEvents = true
        };

        _watcher.Changed += OnConfigFileChanged;
        _watcher.Deleted += OnConfigFileChanged;
        _watcher.Renamed += OnConfigFileRenamed;
    }

    #endregion

    #region Public Methods for IOC Integration

    /// <summary>
    /// 注册类型时调用
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public void RegisterTypeMapping<T>() where T : IOptions, new()
    {
        var typeName = GetConfigTypeName<T>();
        TypeMap[typeName] = typeof(T);
    }

    /// <summary>
    /// 加载或热更新配置（支持依赖注入）
    /// </summary>
    public T LoadOption<T>() where T : IOptions, new()
    {
        var typeName = GetConfigTypeName<T>();
        if (!TypeMap.ContainsKey(typeName))
        {
            TypeMap[typeName] = typeof(T);
        }

        return (T)OptionDic.GetOrAdd(typeName, _ => LoadOrCreateConfig<T>());
    }

    public void SaveOption<T>(T config) where T :  IOptions, new()
    {
        var typeName = GetConfigTypeName<T>();
        var filePath = Path.Combine(ConfigPath, $"{typeName}.json");

        SaveConfigToFile(filePath, config);
    }

    public void SaveAllOption()
    {
        foreach (var item in OptionDic)
        {
            var filePath = Path.Combine(ConfigPath, $"{item.Key}.json");
            SaveConfigToFile(filePath, item.Value);
        }
    }

    #endregion

    #region File Monitoring and Hot Update

    private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
    {
        if (e.ChangeType != WatcherChangeTypes.Changed &&
            e.ChangeType != WatcherChangeTypes.Deleted) return;

        HandleConfigChange(e.FullPath);
    }

    private void OnConfigFileRenamed(object sender, RenamedEventArgs e)
    {
        HandleConfigChange(e.OldFullPath, e.FullPath);
    }

    private async void HandleConfigChange(string filePath, string? newPath = null)
    {
        var fileName = Path.GetFileName(filePath);
        var typeName = Path.GetFileNameWithoutExtension(fileName);

        // 防抖处理（异步取消旧任务）
        if (_fileDebounceTasks.TryGetValue(filePath, out var oldTcs))
        {
            oldTcs.TrySetResult(true); // 取消旧任务
        }

        var tcs = new TaskCompletionSource<bool>();
        _fileDebounceTasks[filePath] = tcs;

        await Task.Delay(DebounceMilliseconds).ConfigureAwait(false);

        if (tcs.Task.IsCompleted) return; // 如果被取消，则不再执行

        try
        {
            // 处理重命名
            if (!string.IsNullOrEmpty(newPath))
            {
                var newTypeName = Path.GetFileNameWithoutExtension(newPath);
                OptionDic.TryRemove(typeName, out _);
                typeName = newTypeName;
                filePath = newPath;
            }

            ReloadConfig(filePath, typeName);
        }
        finally
        {
            _fileDebounceTasks.TryRemove(filePath, out _);
        }
    }

    private void ReloadConfig(string filePath, string typeName)
    {
        try
        {
            if (!File.Exists(filePath)) return;
            if (!TypeMap.TryGetValue(typeName, out var configType))
            {
                Console.WriteLine($"未注册的类型映射: {typeName}");
                return;
            }

            var config = JsonConvert.DeserializeObject(File.ReadAllText(filePath), configType) as IOptions;

            if (config != null)
            {
                var interfaceType = typeof(IOptionsHotReload<>).MakeGenericType(configType);
                if (interfaceType.IsAssignableFrom(configType))
                {
                    // 从缓存中获取 MethodInfo，若不存在则通过反射获取并缓存
                    var hotReloadMethod = HotReloadMethods.GetOrAdd(configType, t =>
                    {
                        return interfaceType.GetMethod("HotReload");
                    });

                    if (hotReloadMethod != null)
                    {
                        hotReloadMethod.Invoke(config, new object[] { config });
                    }
                }
            }


            OptionDic.AddOrUpdate(typeName, config, (_, _) => config);

            Console.WriteLine($"配置已热更新: {typeName}\r\n更新为：\r\n{JsonConvert.SerializeObject(config, Formatting.Indented)}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"配置重载失败 ({typeName}): {ex.Message}");
        }
    }

    #endregion

    #region Helper Methods

    private string GetConfigTypeName<T>()
    {
        var typeName = typeof(T).Name;
        return typeName.EndsWith("Options") ?
            typeName[..^"Options".Length] :
            typeName;
    }

    private T LoadOrCreateConfig<T>() where T : IOptions, new()
    {
        var typeName = GetConfigTypeName<T>();
        var filePath = Path.Combine(ConfigPath, $"{typeName}.json");

        T Config = default(T);

        if (!File.Exists(filePath))
        {
            Config = new T();
            SaveConfigToFile(filePath, Config);
            //return newConfig;
        }
        else
        {
            Config = JsonConvert.DeserializeObject<T>(File.ReadAllText(filePath));
        }

        if (Config != null)
        {
            var configType = typeof(T);

            var interfaceType = typeof(IOptionsInit<>).MakeGenericType(configType);
            if (interfaceType.IsAssignableFrom(configType))
            {
                // 从缓存中获取 MethodInfo，若不存在则通过反射获取并缓存
                var InitMethod = interfaceType.GetMethod("Init");

                if (InitMethod != null)
                {
                    InitMethod.Invoke(Config, new object[] { Config });
                }
            }
        }
        Options.Add(Config);
        return Config;
    }

    private void SaveConfigToFile<T>(string path, T config)
    {
#if DEBUG
        var devRootPath = $"{new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.Parent.Parent.FullName}";
        var dirPath = $"{devRootPath}\\Configuration";
        if (!Directory.Exists(dirPath))
        {
            Directory.CreateDirectory(dirPath);
        }

        string curPath = $"{dirPath}\\{GetConfigTypeName<T>()}.json";

        if(!File.Exists(curPath))
        {
            path = curPath;
        }

#endif

        var json = JsonConvert.SerializeObject(config, Formatting.Indented);
        File.WriteAllText(path, json);
    }


    #endregion
}


/// <summary>
/// 配置容器
/// </summary>
/// <typeparam name="T"></typeparam>
internal sealed class ConfigContainer<T> : IConfigContainer<T> where T : IOptions, new()
{
    private readonly VampirewalCoreAppConfig _configManager;
    public ConfigContainer(VampirewalCoreAppConfig configManager)
    {
        _configManager = configManager;
    }

    public T Value => _configManager.LoadOption<T>();
}